package captor.lib.xalan.extensions;

import java.util.List;

import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.templates.ElemExtensionCall;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xml.dtm.ref.DTMNodeProxy;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import captor.crosscuttingdomainsystem.CrosscuttingDomain;
import captor.crosscuttingdomainsystem.crosscuttinginfo.DomainInfoType.CompatibleDomainsType.DomainType.PreDefinedPointcutsType.AbstractPointcutType;
import captor.domainsystem.BaseDomain;
import captor.modelsystem.CaptorDomain;
import captor.modelsystem.Model;
import captor.projectsystem.ProjectSystem;

/**
 * 
 * Captor Extension.
 * 
 * <p>
 * Xalan XSLT Captor extension element for AOP patterns.
 * </p>
 * 
 * @author Carlos Alberto
 * 
 */

public class CaptorExtension {

	// Return the concrete value for the pointcut defined by 'refName' attribute
	public String abstractpointcut(XSLProcessorContext context,
			ElemExtensionCall elem) {
		String result = "";
		try {
			Element contextNode = (Element) context.getContextNode();
			String refName = elem.getAttribute("refname");

			if ((refName == null) || (refName.equals("")))
				return "";

			final Model model = ProjectSystem.getModelInstance();

			final BaseDomain projectDomain = model.getProject().getBaseDomain();
			CaptorDomain templateDomain = (CaptorDomain) context
					.getTransformer().getParameter("templateDomain");

			result = searchPredefinedValue(projectDomain, templateDomain,
					refName);

			// Search for all 'pointcutatt' elements in the source document
			Document documentRoot = (Document) ((DTMNodeProxy) contextNode)
					.getDocumentElement();
			NodeList nodes = (NodeList) documentRoot
					.getElementsByTagName("pointcutatt");

			// Find the correspondent nodes by 'refName' attribute
			for (int i = 0; i < nodes.getLength(); i++) {
				Element pointcutElement = (Element) nodes.item(i);
				String pointcutRefName = pointcutElement
						.getAttribute("refname");
				if (refName.equals(pointcutRefName)) {
					// Append the concrete value
					NodeList concreteNodes = (NodeList) pointcutElement
							.getElementsByTagName("concretevalue");
					Element concreteElement = (Element) concreteNodes.item(0);
					String concreteValue = concreteElement.getFirstChild()
							.getNodeValue();
					result = concreteValue;
				}
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e.getMessage());
			e.printStackTrace();
		}
		return result;
	}

	public void extensionset(XSLProcessorContext context, ElemExtensionCall elem) {
		try {
			String domainName = elem.getAttribute("crosscuttingdomain");
			if (iscrosscuttedby(domainName)) {
				TransformerImpl transformer = context.getTransformer();
				transformer.executeChildTemplates(elem, true);
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e.getMessage());
			e.printStackTrace();
		}
	}

	public boolean iscrosscuttedby(String domainName) {
		boolean result = false;
		try {
			final Model model = ProjectSystem.getModelInstance();
			final List<CrosscuttingDomain> list = model.getProject()
					.getCrosscuttingDomains();

			for (final CrosscuttingDomain crosscuttingDomain : list) {
				if (crosscuttingDomain.getNameDomain().equals(domainName)) {
					result = true;
					break;
				}
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e.getMessage());
			e.printStackTrace();
		}

		return result;
	}

	// Return the concrete value for the pointcut defined by 'refName' attribute
	public String abstractpointcutlist(XSLProcessorContext context,
			ElemExtensionCall elem) {
		StringBuffer result = new StringBuffer("");
		try {
			Element contextNode = (Element) context.getContextNode();
			String refName = elem.getAttribute("refname");

			if ((refName == null) || (refName.equals("")))
				return "";

			// Search for all 'pointcutlist' elements in the source document
			Document documentRoot = (Document) ((DTMNodeProxy) contextNode)
					.getDocumentElement();
			NodeList nodes = (NodeList) documentRoot
					.getElementsByTagName("pointcutlist");

			// Find the correspondent nodes by 'refName' attribute
			for (int i = 0; i < nodes.getLength(); i++) {
				Element pointcutElement = (Element) nodes.item(i);
				String pointcutRefName = pointcutElement
						.getAttribute("refname");
				if (refName.equals(pointcutRefName)) {
					// Append the concrete value
					NodeList concreteNodes = (NodeList) pointcutElement
							.getElementsByTagName("concretevalue");
					if ((concreteNodes != null)
							&& (concreteNodes.getLength() > 0)) {
						for (int j = 0; j < concreteNodes.getLength(); j++) {
							Element concreteElement = (Element) concreteNodes
									.item(j);
							String concreteValue = concreteElement
									.getFirstChild().getNodeValue();
							if (!result.toString().equals(""))
								result = result.append("\n");
							result = result.append(concreteValue);
						}
					}
				}
			}
		} catch (Exception e) {
			System.out.println("Exception: " + e.getMessage());
			e.printStackTrace();
		}
		return result.toString();
	}

	private String searchPredefinedValue(BaseDomain projectDomain,
			CaptorDomain templateDomain, String pointcutRefName) {

		if ((projectDomain == null) || (templateDomain == null)
				|| (pointcutRefName == null))
			return "";

		if (!templateDomain.isCrosscuttingDomain())
			return "";

		if (templateDomain.getNameDomain()
				.equals(projectDomain.getNameDomain()))
			return "";

		for (CrosscuttingDomain crosscuttingDomain : projectDomain
				.getCompatibleDomains()) {
			if (crosscuttingDomain.getNameDomain().equals(
					templateDomain.getNameDomain())) {
				for (AbstractPointcutType predefPointcut : crosscuttingDomain
						.getPreDefinedPointcuts()) {
					if (predefPointcut.getRefName().equals(pointcutRefName)) {
						return predefPointcut.getConcreteValue();
					}
				}
			}
		}
		return "";
	}
}